#include "proxy/encry/encryserver.h"
#include "proxy/encry/encryserverconn.h"
#include "proxy/cfg/localconfiginstance.h"

namespace proxy{
  
  EncryServer::EncryServer(boost::asio::io_service &accpet_service)
    :is_stop_(true),
    accpet_service_(accpet_service){

  }

  EncryServer::~EncryServer(){
    DLOG(WARNING) << "Delete";
  }

  bool EncryServer::Start(){

    proxy::ConfigInstance *config = proxy::ConfigInstance::GetConfigInstance();
    curl_session_.reset(new proxy::CurlClientSession(accpet_service_, 
      CurlClientSession::SESSION_TYPE_SERVER, config->ping_timeout(), std::string()));

    curl_session_->SignalErrorMessage.connect(
      boost::bind(&EncryServer::OnSessionErrorMessage, shared_from_this(), _1, _2));
    curl_session_->SignalLoginSucceed.connect(
      boost::bind(&EncryServer::OnLoginSucceed, shared_from_this(), _1));
    curl_session_->SignalMessage.connect(
      boost::bind(&EncryServer::OnSessionMessage, shared_from_this(), _1, _2));

    curl_session_->StartServerSession(config->host_url(), config->ping_url());
    return true;
  }

  bool EncryServer::RealyStart(){
    if (!is_stop_){
      DLOG(ERROR) << "The server is running, you can't run agan";
      return false;
    }
    proxy::ConfigInstance *config = proxy::ConfigInstance::GetConfigInstance();
    is_stop_ = false;
    //io_service_work_.push_back(boost::asio::io_service::work(accpet_service_));

    for (int i = 0; i < config->thread_num(); i++){
      io_service_ptr ios(new boost::asio::io_service);
      io_services_.push_back(ios);
      io_service_work_.push_back(boost::asio::io_service::work(*ios));
      thr_grp_.create_thread(boost::bind(&EncryServer::RunThread,
        shared_from_this(), ios));
    }
    accpet_server_.reset(new TcpAccpetServer(
      io_services_, accpet_service_, config->bind_addr(), config->bind_port()));
    accpet_server_->SetSignalAcceptError(
      boost::bind(&EncryServer::OnServerError, shared_from_this(), _1, _2));
    accpet_server_->SetSignalNewConnect(
      boost::bind(&EncryServer::OnNewConnect, shared_from_this(), _1, _2));
    accpet_server_->Start();
    //while (!is_stop_){
    //  try{
    //    accpet_service_.run();
    //  }
    //  catch (std::exception& e) {
    //    std::cerr << e.what() << std::endl;
    //  }
    //}
    return true;
  }

  void EncryServer::Stop(){
    //accpet_server_->Stop();
    is_stop_ = true;
    for (ios_deque::iterator iter = io_services_.begin(); iter != io_services_.end(); iter++){
      (*iter)->stop();
    }
    io_services_.clear();
  }

  void EncryServer::RunThread(io_service_ptr io_service){
    while (!is_stop_){
      try{
        io_service->run();
      }
      catch (std::exception& e) {
        LOG(ERROR) << e.what();
      }
    }
  }

  void EncryServer::OnNewConnect(TcpAccpetServer::Ptr accpet_server,
    socket_ptr connect){
    DLOG(INFO) << "New Connect arrive";
    EncryServerConn::Ptr encry_server_conn(new EncryServerConn(connect, curl_session_));
    proxy::ConfigInstance *config = proxy::ConfigInstance::GetConfigInstance();
    encry_server_conn->Start(config->proxy_addr(), config->proxy_port());
  }

  void EncryServer::OnServerError(TcpAccpetServer::Ptr accpet_server,
    const boost::system::error_code& err){

  }

  void EncryServer::OnPingError(PingClient::Ptr ping_client,
    const boost::system::error_code& err){

    reconnect_timer_.reset(new boost::asio::deadline_timer(
      accpet_service_, boost::posix_time::seconds(PING_TIMEOUT)));
    reconnect_timer_->async_wait(
      boost::bind(&EncryServer::HandleTimeout, shared_from_this(),
      boost::asio::placeholders::error));
  }

  void EncryServer::OnPingSucceed(PingClient::Ptr ping_client){
    RealyStart();
  }

  void EncryServer::HandleTimeout(const boost::system::error_code& err){
    if (err){
      LOG(ERROR) << err.message();
    }
    Start();
  }

  // With Curl Session

  void EncryServer::OnSessionErrorMessage(CurlClientSession::Ptr curl_session,
    int code){
    LOG(ERROR) << code;
  }

  void EncryServer::OnLoginSucceed(CurlClientSession::Ptr curl_session){
    LOG(INFO) << "Login Succeed";
    RealyStart();
  }

  void EncryServer::OnSessionMessage(CurlClientSession::Ptr curl_session,
    std::string message){
    LOG(INFO) << message;
  }
}